/*******************************************************************************
 * Copyright (c) 2011 Irineu Martins de Lima Moura (imlm2).
 * This file is part of JBeat.
 * 
 *     JBeat is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU Lesser General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 * 
 *     JBeat is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU Lesser General Public License for more details.
 * 
 *     You should have received a copy of the GNU Lesser General Public License
 *     along with JBeat.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * Contributors:
 *     Irineu Martins de Lima Moura (imlm2) - initial API and implementation
 ******************************************************************************/
package br.ufpe.cin.imlm2.jbeat.soot.report;

import static br.ufpe.cin.imlm2.util.FormattedIO.printlnTabbed;
import static br.ufpe.cin.imlm2.util.OptionsParser.*;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import soot.SceneTransformer;
import soot.SootClass;
import soot.SootMethod;
import soot.Trap;
import br.ufpe.cin.imlm2.jbeat.soot.common.SootClassResolver;
import br.ufpe.cin.imlm2.jbeat.soot.icpoints.tags.InterconnectingPointsTag;
import br.ufpe.cin.imlm2.jbeat.soot.throwableset.tags.ThrowableSetTag;
import br.ufpe.cin.imlm2.util.Naming;
import br.ufpe.cin.imlm2.util.SystemProperties;

public class ExceptionReporter extends SceneTransformer{

	private File file;

	public ExceptionReporter(String path) {
		super();
		this.setPath(path);
	}

	private void setPath(String p){
		p = p.trim();
		if(!p.equals("") && !p.endsWith(SystemProperties.FILE_SEPARATOR)){
			p = p + SystemProperties.FILE_SEPARATOR;
		}
		this.file = this.checkPaths(p);
	}


	private File checkPaths(String path){
		File dir = new File(path);
		if(!dir.exists()) {
			dir.mkdirs();
		}
		return dir;
	}


	public void reportClasses(Collection<SootClass> classes) {
		try {
			if(isSingleFile()) {
				File f = new File(file.getAbsolutePath() + SystemProperties.FILE_SEPARATOR + "report.txt");
				if(!f.exists()) f.createNewFile();
				Writer reporter = new FileWriter(f,false);
				for (SootClass sootClass : classes) {
					reportClass(reporter, sootClass);
				}
				reporter.flush();
				reporter.close();
			} else {
				for (SootClass sootClass : classes) {
					File f = new File(file.getAbsolutePath() + SystemProperties.FILE_SEPARATOR +
							sootClass.getName() + ".txt");
					if(!f.exists()) f.createNewFile();
					Writer reporter = new FileWriter(f,false);
					reportClass(reporter, sootClass);
					reporter.flush();
					reporter.close();

				}
			}
		} catch (IOException e) {
			throw new RuntimeException("Ocorreu um problema durante a escrita do relatorio",e);
		}
	}



	private void reportClass(Writer reporter, SootClass sootClass)
	throws IOException {
		printlnTabbed(0, sootClass, reporter);
		List<SootMethod> methods = sootClass.getMethods();
		if(!isFullReport()) this.filterAutoGeneratedMethods(methods);
		for (SootMethod sootMethod : methods) {
			if(sootMethod.hasTag(ThrowableSetTag.TAG_NAME)){
				printlnTabbed(1, sootMethod.getDeclaration().replace("<init>", 
						sootMethod.getDeclaringClass().getShortJavaStyleName()), reporter);
				ThrowableSetTag tst = (ThrowableSetTag) sootMethod.getTag(ThrowableSetTag.TAG_NAME);
				printlnTabbed(2, "Throwable Set:", reporter);
				for (SootClass sc : tst.getThrowableSet()) {
					printlnTabbed(3, sc.getName(), reporter);
				}
				if(sootMethod.isConcrete() && sootMethod.hasTag(InterconnectingPointsTag.TAG_NAME)){
					InterconnectingPointsTag ipt = (InterconnectingPointsTag) sootMethod.
						retrieveActiveBody().getTag(InterconnectingPointsTag.TAG_NAME);
					printlnTabbed(2, "Interconnecting points:", reporter);
					for (Trap t : ipt.getInterconnectingPoints()) {
						printlnTabbed(3, t.getException().getName(), reporter);
					}
				}
				printlnTabbed(0, "",reporter);
			}
		}
		printlnTabbed(0, "",reporter);
	}

	private void filterAutoGeneratedMethods(List<SootMethod> methods){
		Iterator<SootMethod> it = methods.iterator();
		while(it.hasNext()){
			SootMethod next = it.next();
			if(Naming.isMethodSynthetic(next.getName())) it.remove();
		}
	}

	@Override
	protected void internalTransform(String phaseName, Map options) {
		Collection<SootClass> classes = SootClassResolver.resolveClasses(getClassReportIncludeList(),
				getClassExcludeList(),getPackageExcludeList(), isReportStandardLibraries());
		System.err.println("Exception report begining");
		this.reportClasses(classes);
		System.err.println("Exception report finished");
	}
}
